﻿using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NewRetail.Application.Components.Account;
using NewRetail.Application.Components.Platform;
using NewRetail.Application.Components.Warehouse;
using NewRetail.Application.Core.Entity;
using NewRetail.Application.Core.SingleStore;
using System;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Domain.Repositories;

namespace NewRetail.Application.Core
{
    public class ReceiptsApprovement : EntityDto
    {
        public int operatorId { get; set; }
        public string receiptNo { get; set; }
    }

    public abstract class ReceiptsBaseController<T> : ReceiptsBaseController<T, PagingRequestDto>
        where T : BaseEntity, IReceiptEntity
    {
        //
    }

    public abstract class ReceiptsBaseController<T, PagingDto> : BaseController<T, PagingDto>
        where T : BaseEntity, IReceiptEntity
        where PagingDto : PagingRequestDto
    {
        protected ReceiptsHelper ReceiptsHelper => this.LazyServiceProvider.LazyGetRequiredService<ReceiptsHelper>();

        [NonAction]
        protected virtual async Task OnChangeApprovementStatus(DbContext db, T item)
        {
            await Task.CompletedTask;
        }

        [NonAction]
        async Task ChangeApprovementStatus(ReceiptsApprovement dto, bool approve)
        {
            var p = new ApproveOrRevoke()
            {
                isAuditing = approve,
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                //remark = string.Empty
            };
            await this.ReceiptsHelper.ApproveOrRevoke<T>(p, this.OnChangeApprovementStatus);
        }

        [HttpPost("approve-receipt")]
        public async Task<ResponseEntity<object>> ApproveReceipts([FromBody] ReceiptsApprovement dto)
        {
            await this.ChangeApprovementStatus(dto, true);
            return new ResponseEntity<object>().SetSuccess();
        }

        [HttpPost("reject-receipt")]
        public async Task<ResponseEntity<object>> RejectReceipts([FromBody] ReceiptsApprovement dto)
        {
            await this.ChangeApprovementStatus(dto, false);
            return new ResponseEntity<object>().SetSuccess();
        }
    }

    public abstract class BaseController : AbpController
    {
        protected IUserAccountService UserAccountService =>
            this.LazyServiceProvider.LazyGetRequiredService<IUserAccountService>();
        protected INoGeneratorService NoGeneratorService =>
            this.LazyServiceProvider.LazyGetRequiredService<INoGeneratorService>();
        protected IEntityHelper EntityHelper =>
            this.LazyServiceProvider.LazyGetRequiredService<IEntityHelper>();

        protected ISingleStoreService SingleStoreService =>
            this.LazyServiceProvider.LazyGetRequiredService<ISingleStoreService>();
    }

    public abstract class BaseController<T> : BaseController<T, PagingRequestDto>
        where T : BaseEntity
    {
        //
    }

    public abstract class BaseController<T, PagingDto> : BaseController
        where T : BaseEntity
        where PagingDto : PagingRequestDto
    {
        protected IRepository<T> Repo => this.LazyServiceProvider.LazyGetRequiredService<IRepository<T>>();

        protected virtual int MaxTake() => 5000;

        [HttpPost("sync-data")]
        public virtual async Task<ResponseEntity<object>> SyncData([FromBody] T[] data)
        {
            data.Should().NotBeNullOrEmpty();

            var svc = this.LazyServiceProvider.LazyGetRequiredService<IDataSyncService>();

            await svc.QueueDataSync(data);

            return new ResponseEntity<object>().SetSuccess();
        }

        [HttpPost("query-all")]
        public virtual async Task<ResponseEntity<T[]>> QueryAll()
        {
            var query = this.SingleStoreService.ApplyToQueryable(this.Repo);
            var data = await query.Take(this.MaxTake()).ToArrayAsync();
            return new ResponseEntity<T[]>().SetData(data);
        }

        protected virtual async Task<IQueryable<T>> FilterQueryPaging(DbContext db, IQueryable<T> query, PagingDto dto)
        {
            await Task.CompletedTask;
            return query;
        }

        protected virtual async Task<IQueryable<T>> OrderQueryPaging(IQueryable<T> query, PagingDto dto)
        {
            query = query.OrderByDescending(x => x.addTime);
            await Task.CompletedTask;
            return query;
        }

        [HttpPost("query-paging")]
        public virtual async Task<ResponseEntity<PagedResultDto<T>>> QueryPaging([FromBody] PagingDto dto)
        {
            dto.Should().NotBeNull();
            (dto.Page >= 0).Should().BeTrue();
            (dto.PageSize > 0 && dto.PageSize < this.MaxTake()).Should().BeTrue();

            var db = await this.Repo.GetDbContextAsync();

            var query = this.SingleStoreService.ApplyToQueryable(this.Repo);

            query = await this.FilterQueryPaging(db, query, dto);

            var data = new PagedResultDto<T>();

            data.TotalCount = await query.CountAsync();

            query = await this.OrderQueryPaging(query, dto);
            query = query.Page(dto.Page, dto.PageSize);
            data.Items = await query.ToArrayAsync();

            return new ResponseEntity<PagedResultDto<T>>().SetData(data);
        }

        /// <summary>
        /// 返回基础数据
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>        
        protected virtual async Task<IQueryable<T>> GetQueryPagingData(PagingDto dto)
        {
            dto.Should().NotBeNull();
            (dto.Page >= 0).Should().BeTrue();
            (dto.PageSize > 0 && dto.PageSize < this.MaxTake()).Should().BeTrue();

            var db = await this.Repo.GetDbContextAsync();

            var query = this.SingleStoreService.ApplyToQueryable(this.Repo);

            query = await this.FilterQueryPaging(db, query, dto);

            query = await this.OrderQueryPaging(query, dto);

            return query;
        }

        [HttpPost("query-by-id")]
        public virtual async Task<ResponseEntity<T>> QueryById([FromBody] IdDto id)
        {
            id.Should().NotBeNull();
            id.Id.Should().NotBeNullOrEmpty();

            var data = (await this.QueryByIds(new[] { id.Id })).Data?.FirstOrDefault();
            if (data == null)
            {
                return new ResponseEntity<T>().SetError("数据不存在");
            }
            else
            {
                return new ResponseEntity<T>().SetData(data);
            }
        }

        [HttpPost("query-by-ids")]
        public virtual async Task<ResponseEntity<T[]>> QueryByIds([FromBody] string[] ids)
        {
            ids.Should().NotBeNullOrEmpty();

            var query = this.SingleStoreService.ApplyToQueryable(this.Repo);
            var data = await query.Where(x => ids.Contains(x.Id)).ToArrayAsync();
            return new ResponseEntity<T[]>().SetData(data);
        }

        protected virtual Task<T> BeforeUpdate(T entity, T data)
        {
            entity.UpdateEntity(data);
            //读取登录用户设置修改人id
            this.EntityHelper.SetUpdationInfo(entity);
            return Task.FromResult(entity);
        }

        protected virtual Task<T> BeforeInsertDataInitial(T data)
        {
            return Task.FromResult(data);
        }

        protected virtual Task<T> BeforeInsert(T data)
        {
            return Task.FromResult(data);
        }

        /// <summary>
        /// 删除前其它操作
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        protected virtual Task<string[]> BeforeDelete(string[] ids)
        {
            return Task.FromResult(ids);
        }

        [HttpPost("save")]
        public virtual async Task<ResponseEntity<T>> Save([FromBody] T data)
        {
            data.Should().NotBeNull();
            if (!string.IsNullOrWhiteSpace(data.Id))
            {
                var entity = await this.Repo.FirstOrDefaultAsync(x => x.Id == data.Id);
                entity.Should().NotBeNull();
                entity = await this.BeforeUpdate(entity, data);

                await this.Repo.UpdateAsync(entity, autoSave: true);
                return new ResponseEntity<T>().SetData(entity).SetSuccess();
            }
            else
            {
                data = await this.BeforeInsertDataInitial(data);
                this.EntityHelper.InitEntity(data);
                data = await this.BeforeInsert(data);

                await this.Repo.InsertAsync(data, autoSave: true);
                return new ResponseEntity<T>().SetData(data).SetSuccess();
            }
        }

        [HttpPost("delete-by-ids")]
        public virtual async Task<ResponseEntity<object>> DeleteByIds([FromBody] string[] ids)
        {
            ids.Should().NotBeNullOrEmpty();
            var db = await this.Repo.GetDbContextAsync();

            var query = db.Set<T>().IgnoreQueryFilters();
            query = this.SingleStoreService.ApplyToQueryable(query);
            //过滤有删除限制的记录ID
            ids = await this.BeforeDelete(ids);
            var data = await query.Where(x => ids.Contains(x.Id)).ToArrayAsync();
            if (data.Any())
            {
                if (typeof(T).IsAssignableTo<ISoftDelete>())
                {
                    foreach (var m in data)
                    {
                        ((ISoftDelete)m).IsDeleted = true;
                        this.EntityHelper.SetUpdationInfo(m);
                    }
                    await db.SaveChangesAsync();
                }
                else
                {
                    await this.Repo.DeleteManyAsync(data, autoSave: true);
                }
            }

            return new ResponseEntity().SetSuccess();
        }
    }
}
